use serde::{Deserialize, Serialize};
use std::{io, path::PathBuf};
use pbkdf2::{pbkdf2_hmac, pbkdf2_hmac_array};
use sha2::Sha256;
use bincode;

use argon2::{
    password_hash::{
        rand_core::OsRng,
        PasswordHash, PasswordHasher, PasswordVerifier, SaltString
    },
    Argon2
};
use anyhow::{Context, Result};

const VERSION: &str = "1.0.0";
const DATA_FILE: &str = "data.data"; // 数据信息
const LOCK_FILE: &str = "lock.data"; // header

#[derive(Serialize, Deserialize)]
struct Header {
    version: String,
    password_hash: String,
}

impl Header {
    fn new(password_hash: &str) -> Self {
        Self {
            version: VERSION.to_string(),
            password_hash: password_hash.to_string(),
        }
    }
}

#[derive(Serialize, Deserialize)]
struct Account {
    name: String,
    description: String,
    username: String,
    password: String,
    icon_data: Vec<u8>,
    email: String,
    url: String,
    remarks: String,
}

pub struct LockBox {
    
}

impl LockBox {
    /// 判断是否已经创建过账户
    pub fn is_account_exists(&self) -> Result<bool> {
        Ok(get_app_dir()?.join(DATA_FILE).is_file())
    }

    /// 创建账户
    pub fn create_account(&self, password: &str) -> Result<()> {
        Ok(())
    }

    /// 验证密码
    pub fn verify_password(&self, password: &str, password_hash: &str) -> Result<()> {
        let parsed_hash = PasswordHash::new(password_hash).map_err(|e| anyhow::anyhow!(e))?;
        Argon2::default().verify_password(password.as_bytes(), &parsed_hash).map_err(|e| anyhow::anyhow!(e))
    }
}

pub fn generate_password_hash(password: &str) -> Result<String> {
    let salt = SaltString::generate(&mut OsRng);
    let argon2 = Argon2::default();
    Ok(argon2.hash_password(password.as_bytes(), &salt).map_err(|e| anyhow::anyhow!(e))?.to_string())

}

// 使用用户的密码和盐值生成aes密钥
pub fn generate_aes_key(password_hash: &str, password: &str) -> Result<(Vec<u8>, Vec<u8>)> {
    let passwd_hash = PasswordHash::new(password_hash).map_err(|e| anyhow::anyhow!(e))?;
    let salt = passwd_hash.salt.ok_or(anyhow::anyhow!("No salt found"))?.as_str().as_bytes();

    // number of iterations
    let rounds = 99;
    let mut aes_key = [0u8; 32];
    pbkdf2_hmac::<Sha256>(password.as_bytes(), salt, rounds, &mut aes_key);

    // iv
    let mut iv = [0u8; 16];
    pbkdf2_hmac::<Sha256>(password.as_bytes(), salt, rounds, &mut iv);

    Ok((aes_key.to_vec(), iv.to_vec()))
}

fn get_app_dir() -> Result<PathBuf, io::Error> {
    std::env::current_exe()?.parent().map(|p| p.to_path_buf()).ok_or(io::Error::new(io::ErrorKind::NotFound, "Not found"))
}

pub fn aes_encrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
    // Encrypt data using AES key
    data.to_vec() // Replace with actual encryption logic
}

pub fn aes_decrypt(key: &[u8], data: &[u8]) -> Vec<u8> {
    // Decrypt data using AES key
    data.to_vec() // Replace with actual decryption logic
}

pub fn save_accounts(accounts: &[Account], key: &[u8]) -> Result<(), io::Error> {
    // Save accounts to file
    Ok(())
}

pub fn load_accounts(key: &[u8]) -> Result<Vec<Account>, io::Error> {
    // Load accounts from file
    Ok(vec![])
}


#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn test_generate_password_hash() {
        let password = "password";
        let password_hash = generate_password_hash(password).unwrap();

        let lock_box = LockBox {};
        assert!(lock_box.verify_password(password, &password_hash).is_ok());
    }

    #[test]
    fn test_generate_aes_key() {
        let password = "password";
        let password_hash1 = generate_password_hash(password).unwrap();
        let password_hash2 = generate_password_hash(password).unwrap();
        assert_ne!(password_hash1, password_hash2);  // 同样的密码每次生成的哈希不同

        let aes_key_pair0 = generate_aes_key(&password_hash1, password).unwrap();
        let aes_key_pair1 = generate_aes_key(&password_hash1, password).unwrap();

        assert_eq!(aes_key_pair0.0.len(), 32);
        assert_eq!(aes_key_pair0.1.len(), 16);
        assert_eq!(aes_key_pair1.0.len(), 32);
        assert_eq!(aes_key_pair1.1.len(), 16);

        assert_eq!(aes_key_pair0, aes_key_pair1);  // 同样的哈希每次生成相同的aes密钥
    }
}
